/*
AI Code for Invasion Force - an Explore/Conquer Strategic Wargame
Copyright (C) 1996  Brannen Hough

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
	cyber_interface.c -- artificial intelligence module for Empire II

*/

/* This file contains all the routines associated with interfacing 
    with the main game engine - basically at two places (1) selecting
    production for new cities (and cities that just built something)
    and (2) the player's mainloop (where all the units get moved, etc.)
    Cleanup (when the game is over) has also been stuck in here. 
*/

#include "global.h"

#define  DEBUG_AI_INT(string)  if (!rtEZRequestTags(string,"Continue|Abort", \
     NULL,NULL, RTEZ_Flags,EZREQF_CENTERTEXT,RT_Window,map_window, \
     RT_ReqPos,REQPOS_CENTERWIN,RT_LockWindow,TRUE,TAG_END )) \
     { if (AIhandle != NULL)  unpost_it(AIhandle); AIhandle = NULL; \
     clean_exit(0, NULL); }

/***************************************************************
*************** Production Interface   *************************
***************************************************************/

/* This routine will set up the needed production for all Governors. */
void  set_automated_production (struct City *metro)
{
    int     AI_type = PLAYER.aggr;
    struct  GovNode*  Gov = NULL;

   /* Now let's explore the hex we start with.  If we just took this
       city, or are starting out with it, this is important. */
    explore_at_hex(player,metro->col,metro->row,INVISIBLE,FALSE);

    /* We do the next in this manner so that other computer opponents
       can be added later, up to the ten maximum. Note we have deliberate
       fall-throughs so that unsupported AI types will default to the 
       'best' supported type. */
    switch( AI_type ) {
    case 10:
    case 9:
    case 8:
    case 7:
    case 6:
    case 5:
    case 4:
    case 3:
	Gov = AI3_locate_gov( metro );
	/* And let the (perhaps new) Gov take a look around */
    explore_at_hex( player, metro->col, metro->row, INVISIBLE, FALSE);
	AI1_do_one_histogram (Gov);
	AI3_set_gov_mode (Gov);
	AI3_set_gov_prod( metro, Gov );
	if( PLAYER.aggr != 3 ) PLAYER.aggr = 3;
	break;
    case 2:
        Gov = AI2_locate_gov( metro );
        AI2_set_gov_prod( metro, Gov );
	if( PLAYER.aggr != 2 ) PLAYER.aggr = 2;
        break;
    case 1:
	Gov = AI1_locate_gov( metro );
	AI1_set_gov_prod( metro, Gov );
	if( PLAYER.aggr != 1 ) PLAYER.aggr = 1;
	break;
    default:
	    DEBUG_AI("Error in set_automated_prod.  Aggressiveness out of range.")
	    break;
    } /* End switch */
    return;
}


int	do_computer_city_production ()
{
    struct City *metro = (struct City *)city_list.mlh_Head;
    int 		ctr = 0;
    /* This routine will duplicate much of do_cities_production.  These two
    	should be put back together in the future.  For now, this works just
	fine. 
	*/

    for ( ; metro->cnode.mln_Succ; metro = 
          (struct City *)metro->cnode.mln_Succ)
      if (metro->owner==player) {
         metro->unit_wip += (metro->industry*PLAYER.prod)/50;
         explore_at_hex(player,metro->col,metro->row,INVISIBLE,TRUE); 
         if (metro->unit_wip >= wishbook[metro->unit_type].build) {
	     /* We finished a new unit */
	     make_new_unit( metro );
	     /* Now we call set_automated_production to set a new production
		for this city 
		*/
	     ctr++;
	     set_automated_production (metro);
         } /* End if > Wishbook */
      } /* End if owner of city */
    /* End for loop */

    return (ctr);  /* Don't know if this is usefull info or not right now. */
}


void   make_new_unit( struct City* metro)
{
    struct GovNode *Gov = NULL;
    struct GovNode *Gov2 = NULL;
    int			Done;

    struct Unit *new_unit = AllocVec((int)sizeof(*new_unit),MEMF_CLEAR);

    metro->unit_wip = 0;

    /* build the unit */
    new_unit->col = metro->col;
    new_unit->row = metro->row;
    new_unit->owner = player;
    new_unit->type = metro->unit_type;
    new_unit->move = unit_speed(new_unit);
    new_unit->damage = 0;
    new_unit->attacks = 0;
    new_unit->cargo = 0;
    new_unit->ship = NULL;
    new_unit->orders = NULL;
    new_unit->fuel = wishbook[new_unit->type].range;
    
    /* DEBUG_AI("Built new unit!") */

    /* We need to name the unit so that we can tell what governor 
       owns it. This is done to keep within the unit structures
       as defined and not add any more elements (so far).
       */
    Done = 0;
    for ( Gov = (struct GovNode *)GovList.mlh_Head; (Gov->gnode.mln_Succ) 
	      && (!Done); Gov = (struct GovNode *)Gov->gnode.mln_Succ) {
	/* Check for the right coordinates */
	if ((Gov->x == metro->col) && (Gov->y == metro->row)
	    && (Gov->owner == player)
	    && ((Gov->type == GOV_CITY) || (Gov->type == GOV_PORT)
		|| (Gov->type == GOV_ISLAND) )) {
	        /* We found the right governor, now let's
	        set the name correctly
	        */
	        sprintf (outbuf, "%ld / %ld", Gov->req.req_gov, NewUnit++);
    	    name_unit(new_unit, outbuf);
	        Done = 1;
	        sprintf(outbuf, "Built new %s", UnitString[new_unit->type]);
    	    DEBUG_AI(outbuf)
	        sprintf(outbuf, "Naming unit '%s' (%ld)", new_unit->name,
		        Gov->req.req_gov);
    	    DEBUG_AI(outbuf)
            /* And, lets update the data for the owning governor */
            for(Gov2 = (struct GovNode *)GovList.mlh_Head;
                (Gov2->gnode.mln_Succ);
    	        Gov2 = (struct GovNode *)Gov2->gnode.mln_Succ) {
                if( Gov2->ID == Gov->req.req_gov ) {
                        Gov2->hist.TotalMyUnits++;
                        Gov2->hist.UnitCounts[new_unit->type]++;
                }
            } /* end for */
        } /* End if right coordinates and owner */
    }  /* End for loop */
    if ((!Done) || (new_unit->name == NULL)) {
	name_unit(new_unit, "UNNAMED");
	DEBUG_AI("Problem! Have no name for newly built unit")
	if (!Done) DEBUG_AI("Did not find correct Governor")
	sprintf (outbuf, "%s Unit %s is at %ld, %ld",
		 UnitString[new_unit->type], new_unit->name, new_unit->col,
		 new_unit->row);
	DEBUG_AI(outbuf)
    }
    AddTail((struct List *)&unit_list,(struct Node *)new_unit);
}



/*************************************************************
***************  Game Play Routines  *************************
*************************************************************/

void  computer_player_moves ()
{
    int          AI_type = PLAYER.aggr;
    int          new_units;
    struct Unit *unit = (struct Unit *)unit_list.mlh_Head;

    /**********************************/
    int          BeforeTotal;
    int          i, j;
    int          AfterTotal;
    /**********************************/

    /* This routine is called by the game mechanics to do the computer
    	player's moves, production, repair, etc.
	*/

    /* First, let's put up a sticky note to say who we are */
    sprintf (outbuf, "Player %ld Moves", player);
    AIhandle = post_it (outbuf);
    
    /* sprintf (outbuf, "Computer player moves for turn #%ld", turn); */
    /* DEBUG_AI(outbuf) */

    /* update all unit moves and explore around units */
    for (; unit->unode.mln_Succ; unit = (struct Unit *)unit->unode.mln_Succ)
         if (unit->owner==player) {
            unit->move = unit_speed(unit);
            unit->attacks = 0;
            explore_at_hex(player,unit->col,unit->row,INVISIBLE,FALSE); 
         } /* End if my units */
    /* End For Loop */
    /* DEBUG_AI("Updated all units movement points") */


    /*********************************************/
    /* Now let's do a little error checking - we will total the units in
       existance both before and after each computer player's turn.  This
       should help to pinpoint the problems with the unit list and correct
       them.
       */
    BeforeTotal = count_nodes( &unit_list );
    for( i=1; i<9; i++)
        for( j=0; j<11; j++ )
        	BeforeTotal += roster[i].ulc[j];
    /**********************************************/


    /* update his production for this turn */
    new_units = do_computer_city_production();
    /* DEBUG_AI("Done with production phase") */


    /**********************************************/
    BeforeTotal += new_units;
    /**********************************************/


    /* We do the next in this manner so that other computer opponents
       can be added later, up to the ten maximum. Note we have deliberate
       fall-throughs so that unsupported AI types will default to the 
       'best' supported type. */
    switch( AI_type ) {
    case 10:
    case 9:
    case 8:
    case 7:
    case 6:
    case 5:
    case 4:
    case 3:
	AI3_play_turn( new_units );
	break;
    case 2:
        AI2_play_turn( new_units );
        break;
    case 1:
    	AI1_play_turn( new_units );
	    break;
    default:
	    DEBUG_AI("Error in computer_player_moves().  Aggressiveness out of range.")
    	break;
    } /* End switch */

    
    /* do some housekeeping at end of turn
       here I want to check for any ships that need repair
       */
    unit=(struct Unit *)unit_list.mlh_Head;
    for (; unit->unode.mln_Succ; unit=(struct Unit *)unit->unode.mln_Succ)
         if (unit->damage > 0 && unit->move > 50)
            if (unit->owner==player && city_hereP(unit->col,unit->row))
               unit->damage--;
    /* End if, End if, End for */
    /* DEBUG_AI("Done with unit repair") */



    /*******************************************/
    /* Now, for the after total */
    AfterTotal = count_nodes( &unit_list );
    for( i=1; i<9; i++ )
        for( j=0; j < 11; j++ )
	        AfterTotal += roster[i].ulc[j];
    if( AfterTotal != BeforeTotal ) {
	/* Big trouble! the unit list got fouled! */
	sprintf( outbuf, 
	    "ERROR! Unit list fouled. Before:%ld, After:%ld, new_units:%ld", 
	     BeforeTotal, AfterTotal, new_units );
	DEBUG_AI_INT( outbuf )
    }
    /**********************************************



    /* All Done.  Let's remove the sticky note */
    unpost_it(AIhandle);
    AIhandle = NULL;

    return;
}

/*************************************************************
*************** Initialization and Cleanup *******************
*************************************************************/

// This routine is to deallocate the memory for computer opponents
void    cleanup_computer()
{
	//calc_move (MOVE_CLEANUP, NULL, 0, 0, 0, 0);

	// We need to clean out and delete all the Governors
	nuke_list (&GovList);
 
    // Take out Post-it if up
    if (AIhandle != NULL)  unpost_it(AIhandle);
    AIhandle = NULL;

    // And just for the heck of it, reset the numbering scheme
    NewGov = 12;
    NewUnit = 1000;

    return;
}


/*************************************************************
***************  Saving and Restoring Files  *****************
*************************************************************/


int  SaveAIPlayers( BPTR file, char* err )
{
  // the governor list
   {
      struct GovNode *gov = ( struct GovNode* ) GovList.mlh_Head;
      int num_govs = count_nodes( &GovList );

      Write( file, &num_govs, (long) sizeof( num_govs ) );
      for (; gov->gnode.mln_Succ; gov = ( struct GovNode* )gov->gnode.mln_Succ)
         // We want to save everything in each governor node except the cnode
         // so that has to stay in the front, and this needs to point to the
         // first item of data AFTER the cnode, which is the type for the
         // governors
         Write( file, &gov->type, (long)(sizeof( *gov )-sizeof(gov->gnode)) );
   }

  return 0;
}


int  LoadAIPlayers( BPTR file, char*err )
{
   int  ctr;

   // restore the AI players' governors list
   int num_govs;

   (void)Read( file, &num_govs, sizeof( num_govs ) );
   if ( num_govs > 0 ) {
      struct GovNode *gov;

      for( ctr=1; ctr <= num_govs; ctr++ ) {
         gov = AllocVec( sizeof( *gov ), MEMF_CLEAR );
         if ( gov == NULL ) {
            strcpy( err, "Unable to allocate RAM for governors." );
            return -1;
         } // end if
         (void)Read( file, &gov->type, sizeof( *gov )- sizeof( gov->gnode ));
         AddTail( (struct List* )&GovList, (struct Node *)gov );
     } // End for
   } // end if
   return 0;
}